import pygame
import random
#全局常量定义
SCREEN_RECT=pygame.Rect(0,0,480,700)#游戏主窗口矩形区域
FRAME_INTERVAL=10                   #逐帧动画间隔帧数
HERO_BOMB_COUNT=3                   #英雄默认炸弹数量

HERO_DEFAULT_MID_BOTTOM=(SCREEN_RECT.centerx,#英雄默认初始位置
                         SCREEN_RECT.bottom-90)
HERO_DEAD_EVENT=pygame.USEREVENT    #英雄牺牲事件
HERO_POWER_OFF_EVENT = pygame.USEREVENT+1    #取消英雄无敌事件
HERO_FIRE_EVENT = pygame.USEREVENT + 2 #英雄发射子弹事件
THROW_SUPPLY_EVENT = pygame.USEREVENT+3 #投放道具事件
BULLET_ENHANCED_OFF_EVENT = pygame.USEREVENT+4 #关闭子弹增强事件

class GameSprite(pygame.sprite.Sprite):
    res_path='./res/images/'
    def __init__(self,image_name,speed,*group):
        """初始化精灵对象"""
        #调用父类方法，把当前精灵对象放到精灵组里
        super(GameSprite,self).__init__(*group)

        #创建图片
        self.image=pygame.image.load(self.res_path + image_name)
        #获取矩形
        self.rect=self.image.get_rect()
        #设置移动速度
        self.speed = speed
        #生成遮罩属性，提高碰撞检测的执行效率
        self.mask=pygame.mask.from_surface(self.image)

    def update(self,*args):
        """更新元素数据"""
        self.rect.y += self.speed

class Background(GameSprite):
    def __init__(self,is_alt,*group):
        """如果is_alt为True 则这精灵显示在窗口上方，False，则显示在窗口的内部"""
        super(Background,self).__init__('background.png',1,*group)
        if is_alt:
            self.rect.y = -self.rect.h


    def update(self,*args):
        super(Background,self).update(*args)

        #如果图片已经滚动到底部，则立即回到窗口的最上面，重新显示

        if self.rect.y > self.rect.h:
            self.rect.y = -self.rect.y

class StatusButton(GameSprite):
    """状态按钮精灵"""

    def __init__(self,image_names,*groups):
        """image_names接收一个元组，元组的0下标必须时暂停的图片，1下表必须是运行的图片"""
        super(StatusButton,self).__init__(image_names[0],0,*groups)

        #准备用于切换显示的两张图片
        self.images=[pygame.image.load(self.res_path + name)
                     for name in image_names]
    def swith_status(self,is_pause):
        """根据是否暂停，切换要使用的图片对象"""
        self.image=self.images[1 if is_pause else 0]

class Label(pygame.sprite.Sprite):
    """标签精灵类"""
    file_path = './res/font/MarkerFelt.ttc'

    def __init__(self, text,size,color,*groups):
        """初始化标签精灵的数据"""
        super(Label, self).__init__(*groups)

        #创建字体对象"
        self.font=pygame.font.Font(self.file_path,size)

        #字体颜色
        self.color=color

        #精灵属性
        self.image=self.font.render(text,True,self.color)
        self.rect = self.image.get_rect()

    def set_text(self,text):
        """更新显示的文本内容"""
        self.image=self.font.render(text,True,self.color)
        self.rect=self.image.get_rect()

class Plane(GameSprite):
    """飞机精灵"""
    def __init__(self, normal_names,speed,hp,value,wav_name,hurt_name,destroy_names,* groups):
        """飞机类的初始化"""
        super(Plane,self).__init__(normal_names[0],speed,*groups)

        #飞机基本属性
        self.hp = hp #当前生命值
        self.max_hp = hp #初始生命值
        self.value=value  #分值
        self.wav_name=wav_name  #音效名
        #飞机要显示的图片
        self.normal_images=[pygame.image.load(self.res_path+name)for name in normal_names]#正常状态
        self.normal_index=0#正常状态图像索引
        self.hurt_image=pygame.image.load(self.res_path + hurt_name)#受伤图像
        self.destroy_images = [pygame.image.load(self.res_path +name) for name in destroy_names]#摧毁状态图像的列表
        self.destroy_index = 0#摧毁状态索引

    def reset_plane(self):
        """重置飞机"""
        self.hp=self.max_hp

        self.normal_index=0
        self.destroy_index=0

        self.image=self.normal_images[0]


    def update(self,*args):
        """更新状态，准备下一次要显示的内容"""
        #判断是否要更新
        if  not args[0]:
            return

        if self.hp==self.max_hp:
            #切换要显示的图片
            self.image = self.normal_images[self.normal_index]
            #计算一次显示的索引
            count=len(self.normal_images)
            self.normal_index = (self.normal_index + 1) % count
        elif self.hp>0:
            #受伤
            self.image=self.hurt_image
        else:
            #死亡
            if self.destroy_index<len(self.destroy_images):
                self.image = self.destroy_images[self.destroy_index]
                count=len(self.destroy_images)
                self.destroy_index+=1
            else:
                self.reset_plane()

class Enemy(Plane):
    """敌人飞机"""
    def __init__(self,kind,max_speed,*groups):
        """初始化敌人飞机"""
        self.kind = kind
        self.max_speed = max_speed

        if kind == 0:
            #小敌机
            super(Enemy, self).__init__(
                ['enemy1.png'],1,1,1000,'enemy1_down.wav','enemy1.png',
                ['enemy1_down%d.png' % i  for i in range(1,5)],
                *groups

            )
        elif kind == 1:
            #中敌机
            super(Enemy, self).__init__(
                ['enemy2.png'], 1, 6, 6000, 'enemy2_down.wav', 'enemy2_hit.png',
                ['enemy2_down%d.png' % i for i in range(1, 5)],
                *groups

            )
        elif kind == 2:
            #大敌机
            super(Enemy, self).__init__(
                ['enemy3_n1.png','enemy3_n2.png'], 1, 15, 15000, 'enemy3_down.wav', 'enemy3_hit.png',
                ['enemy3_down%d.png' % i for i in range(1, 7)],
                *groups

            )
            #初始化飞机时，要让飞机随机的选择一个位置
        self.reset_plane()

    def reset_plane(self):
        """重置敌人飞机"""
        super(Enemy,self).reset_plane()

        #敌人飞机的数据重置
        x=random.randint(0,SCREEN_RECT.w-self.rect.w)
        y=random.randint(0, SCREEN_RECT.h-self.rect.h)-SCREEN_RECT.h

        self.rect.topleft=(x,y)
        #重置飞机的飞行速度
        self.speed=random.randint(1,self.max_speed)

    def update(self,*args):
        """更新飞机的位置信息"""
        super(Enemy,self).update(*args)
        #根据血量判断是否还要移动
        if self.hp>0:
            self.rect.y+=self.speed

        #如果移动后已经到了屏幕之外，则需要重置飞机
        if self.rect.y>=SCREEN_RECT.h:
            self.reset_plane()


class Hero(Plane):
    """英雄飞机"""

    def __init__(self, *groups):
        """初始化英雄飞机"""
        self.is_power=False  #是否无敌
        self.bomb_count = HERO_BOMB_COUNT #炸弹数量
        self.bullets_kind=0 # 子弹类型
        self.bullets_group=pygame.sprite.Group() #子弹精灵组

        super(Hero,self).__init__(('me1.png','me2.png'),
                          5,1,0,'me_down.wav','me1.png',
                          ['me_destroy_%d.png' % x  for x in range(1,5)],
                                  *groups)
        self.rect.midbottom = HERO_DEFAULT_MID_BOTTOM #创建好飞机之后要设置为屏幕底部中间

        pygame.time.set_timer(HERO_FIRE_EVENT,200)  #创建玩家飞机之后每0.2秒激活一次发射子弹事件

    def update(self,*args):
        """

        args的1号下标说明了是否要更新一下帧动画，1号说明玩家飞机水平方向移动基数，
        2号玩家说明玩家飞机垂直方向的移动基数
        """
        super(Hero,self).update(*args)
        if len(args)!=3 or self.hp<=0:
            return

        #屏幕边缘的位置修正
        self.rect.x+=args[1]*self.speed
        self.rect.x=0 if self.rect.x<0 else self.rect.x
        if self.rect.right>SCREEN_RECT.right:
            self.rect.right=SCREEN_RECT.right

        self.rect.y+=args[2]*self.speed
        self.rect.y=0 if self.rect.y<0 else self.rect.y
        if self.rect.bottom>SCREEN_RECT.bottom:
            self.rect.bottom=SCREEN_RECT.bottom

    def blowup(self,enemies_group):
        """炸毁所有敌机 并返回得到的总分值"""
        #判断是否能够发起引爆
        if self.bomb_count<=0 or self.hp<=0:
            return
        #引爆所有敌机， 累计得分
        self.bomb_count -=1
        score=0
        count=0
        for enemy in enemies_group.sprites():
            if enemy.rect.bottom>0:
                score+=enemy.value
                enemy.hp=0
                count+=1
        print('炸毁了%d架敌机，获取得分%d'%(count,score))
        return score


    def reset_plane(self):
        """重置玩家飞机数据"""
        super(Hero,self).reset_plane()

        self.is_power= True
        self.bomb_count=HERO_BOMB_COUNT
        self.bullets_kind=0
        #发布事件，让游戏主逻辑更新界面
        pygame.event.post(pygame.event.Event(HERO_DEAD_EVENT))

        #发布定时事件
        pygame.time.set_timer(HERO_POWER_OFF_EVENT,3000)

    def fire(self,display_group):
        """英雄飞机发射一轮新的子弹"""
        #准备子弹要显示的组
        groups=(display_group,self.bullets_group)

        #创建新子弹并定位
        for i in range(3):
            bullet1 = Bullet(self.bullets_kind,*groups)
            y = self.rect.y - i*15
            if self.bullets_kind == 0:
                 bullet1.rect.midbottom=(self.rect.centerx,y)
            else:
                bullet1.rect.midbottom=(self.rect.centerx-20,y)

                bullet2=Bullet(self.bullets_kind,*groups)
                bullet2.rect.midbottom=(self.rect.centerx+20,y)




class Bullet(GameSprite):
    """子弹类"""
    def __init__(self,kind,*group):
        """初始化子弹数据"""
        image_name= 'bullet1.png' if kind==0 else 'bullet2.png'
        super(Bullet, self).__init__(image_name,-12,*group)
        self.damage=1    #杀伤力

    def update(self,*args):
        """更新子弹数据"""
        super(Bullet, self).update(* args)
        #飞出屏幕之外，则需要销毁子弹
        if self.rect.bottom < 0:
            self.kill()


class Supply(GameSprite):
    """道具精灵"""
    def __init__(self, kind, * group):
        """初始化道具属性"""
        image_name= 'bomb_supply.png' if kind== 0 else "bullet_supply.png"
        super(Supply, self).__init__(image_name,5,*group)

        self.kind=kind #道具类型
        self.wav_name='get_bomb.wav' if kind == 0 else 'get_bullet.wav'#道具音效

        self.rect.bottom=SCREEN_RECT.h #道具初始位置

    def update(self, * args):
        """修改道具位置"""

        if self.rect.y>SCREEN_RECT.h:  #如果移动到屏幕之外不需要要继续移动
            return
        super(Supply, self).update(* args)

    def throw_supply(self):
        """投放道具"""
        self.rect.bottom = 0  #移动到窗口的顶部
        self.rect.x=random.randint(0,SCREEN_RECT.w-self.rect.w)













